package com.ai_chat.sys.controller;

import com.ai_chat.common.util.JwtUtil;
import com.ai_chat.common.vo.CodeType;
import com.ai_chat.common.vo.Result;
import com.ai_chat.sys.entity.Qa;
import com.ai_chat.sys.entity.User;
import com.ai_chat.sys.service.impl.QaServiceImpl;
import com.ai_chat.sys.service.impl.UserServiceImpl;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestController
@CrossOrigin
@RequestMapping("/qa")
public class QaController {

    @Autowired
    private QaServiceImpl qaService;

    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private OpenAiChatModel chatClient;

    @Autowired
    private JwtUtil jwtUtil;

    //流式输出，produces要设成TEXT_EVENT_STREAM_VALUE
    @GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam("Authorization") String token,
                                                    @RequestParam("topicId") Integer topicId,
                                                    @RequestParam("message") String message){

        User user;
        try{
            user = jwtUtil.parseToken(token, User.class);
        }catch (Exception e){
            e.printStackTrace();

            //token过期或无效，则返回自定义event类型的sse消息，提示前端登录凭证失效，请重新登录
            return Flux.just(
                    ServerSentEvent.<String>builder()
                            .event("tokenInvalid")
                            .data("Token expired or invalid")
                            .build()
            );
        }

        User chatUser = userService.getUserTokenByUserId((user.getId()));

        if(chatUser.getToken() <= 0){
            return Flux.just(
                    ServerSentEvent.<String>builder()
                            .event("insufficientToken")
                            .data("Insufficient token balance")
                            .build()
            );
        }

        StringBuilder stringBuilder = new StringBuilder();
        Flux<ServerSentEvent<String>> answerFlux = chatClient.stream(message)
                //确保返回的数据顺序性及完整性
                .flatMapSequential(Flux::just)
                //将流中的每个元素转换成SSE对象
                .map(messagePart -> {
                    //将流中所有的元素拼接起来，以便后续落地到数据库
                    stringBuilder.append(messagePart);

                    return ServerSentEvent.<String>builder()
                            .data(messagePart)
                            .build();
                })
                //发送完毕或前端意外中断，将已经产生的数据存储到数据库
                .doFinally(signalType -> {
                    //数据库insert
                    Qa q = new Qa();
                    q.setqFlag(true);
                    q.setTopicId(topicId);
                    q.setContent(message);

                    Qa a = new Qa();
                    a.setqFlag(false);
                    a.setTopicId(topicId);
                    a.setContent(stringBuilder.toString());

                    qaService.insertQRecord(q);
                    qaService.insertARecord(a);

                    chatUser.setToken(chatUser.getToken() - stringBuilder.toString().length());
                    //更新token
                    userService.updateUserToken(chatUser);
                });

        return answerFlux.concatWith(Flux.just(
                ServerSentEvent.<String>builder()
                        .event("finish")
                        .data("Chat stream finished")
                        .build()
        ));
    }

    @GetMapping("/getQaListInTopic")
    public Result<List<Map<String,Object>>> getQaListInTopic(@RequestHeader("Authorization") String token,
                                                             @RequestParam("topicId") Integer topicId){
        User user;
        try{
            user = jwtUtil.parseToken(token, User.class);
        }catch (Exception e){
            e.printStackTrace();
            return Result.fail(CodeType.TOKEN_EXPIRED_OR_INVALID);
        }

        List<Qa> qaList = qaService.getQaListInTopic(topicId);
        List<Map<String,Object>> data = new ArrayList<>();
        for (Qa qa : qaList){
            Map<String,Object> qaMap = new HashMap<>();
            qaMap.put("qFlag",qa.getqFlag());
            qaMap.put("content",qa.getContent());

            data.add(qaMap);
        }
        return Result.success(data);
    }

}